(* This file is part of IsoType.
 *
 * IsoType is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * IsoType is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with IsoType.  If not, see <http://www.gnu.org/licenses/>.
 *)

let sum_float = List.fold_left (+.) 0.0
let max_float =
  function
      [] -> assert false (* IMPOSSIBLE *)
    | x :: xs -> List.fold_left max x xs
let sqr_float x = x *. x
let differences = List.map2 (-.)

let euclidean xs ys = sqrt (sum_float (List.map sqr_float (differences xs ys)))
let city_block xs ys = sum_float (List.map abs_float (differences xs ys))
let chebyshev xs ys = max_float (List.map abs_float (differences xs ys))
let sorensen xs ys = city_block xs ys /. (sum_float xs +. sum_float ys)
let gower xs ys = city_block xs ys /. float_of_int (List.length xs)
let soergel xs ys = city_block xs ys /. sum_float (List.map2 max xs ys)
let canberra xs ys = sum_float (List.map2 (fun x y -> abs_float (x -. y) /. (x +. y)) xs ys)
let sharing xs ys = min
  (sum_float (List.map (max 0.0) (differences xs ys)) /. sum_float xs)
  (sum_float (List.map (max 0.0) (differences ys xs)) /. sum_float ys)

let distance_functions =
    ["Euclidean", euclidean;
     "CityBlock", city_block;
     "Chebyshev", chebyshev;
     "Sorensen", sorensen;
     "Gower", gower;
     "Soergel", soergel;
     "Canberra", canberra;
     "Sharing", sharing;
    ]

let names = List.map fst distance_functions

let distance_function name =
  try
    List.assoc name distance_functions
  with
      Not_found ->
	prerr_endline (Printf.sprintf "Unknown distance `%s'" name);
	exit (-1)

